import React, { useState, useRef } from 'react'
import classNames from 'classnames'
import { useTranslation } from 'react-i18next'
import { animated, useSpring } from '@react-spring/web'
import { RiCloseLine } from '@remixicon/react'
import { useIsomorphicLayoutEffect } from '../../hooks/use-isomorphic-layout-effect'
import { useUnmountedRef } from '../../hooks/use-unmounted-ref'
import { useInnerVisible } from '../../hooks/use-inner-visible'
import { useLockScroll } from '../../hooks/use-lock-scroll'
import { withStopPropagation } from '../../utils/with-stop-propagation'
import { withNativeProps } from '../../utils/native-props'
import { ShouldRender } from '../../utils/should-render'
import { renderToContainer } from '../../utils/render-to-container'
import { Mask } from '../Mask'
import type { CenterPopupProps } from './type'

const classPrefix = 'rt-center-popup'

export const CenterPopup: React.FC<CenterPopupProps> = (props) => {
  const {
    'aria-label': ariaLabel,
    afterShow,
    afterClose,
    bodyClassName,
    bodyStyle,
    closeOnMaskClick = true,
    destroyOnClose = false,
    disableBodyScroll = true,
    forceRender = false,
    getContainer = null,
    mask = true,
    maskClassName,
    maskStyle,
    onMaskClick,
    onClose,
    role,
    showCloseButton = false,
    stopPropagation = ['click'],
    visible = false,
    children,
  } = props

  const { t: translate } = useTranslation(['common'])

  const ref = useRef<HTMLDivElement>(null)

  const unmountedRef = useUnmountedRef()

  const [active, setActive] = useState<boolean>(visible)

  useLockScroll(ref, disableBodyScroll && active)

  const style = useSpring({
    scale: visible ? 1 : 0.5,
    opacity: visible ? 1 : 0,
    config: {
      mass: 1.2,
      tension: 200,
      friction: 25,
      clamp: true,
    },
    onRest: () => {
      if (unmountedRef.current) return
      setActive(visible)
      visible ? afterShow?.() : afterClose?.()
    },
  })

  useIsomorphicLayoutEffect(() => {
    if (visible) {
      setActive(true)
    }
  }, [visible])

  const maskVisible = useInnerVisible(active && visible)

  const body = (
    <div
      className={classNames(`${classPrefix}-body`, bodyClassName)}
      style={bodyStyle}
    >
      {children}
    </div>
  )

  const node = withStopPropagation(
    stopPropagation,
    withNativeProps(
      props,
      <div
        className={classPrefix}
        style={{
          display: active ? undefined : 'none',
          pointerEvents: active ? undefined : 'none',
        }}
      >
        {mask && (
          <Mask
            className={classNames(`${classPrefix}-mask`, maskClassName)}
            disableBodyScroll={disableBodyScroll}
            destroyOnClose={destroyOnClose}
            forceRender={forceRender}
            onMaskClick={(e) => {
              onMaskClick?.(e)
              if (closeOnMaskClick) {
                onClose?.()
              }
            }}
            stopPropagation={stopPropagation}
            style={maskStyle}
            visible={maskVisible}
          />
        )}
        <div
          className={`${classPrefix}-wrap`}
          role={role}
          aria-label={ariaLabel}
        >
          <animated.div
            style={{
              ...style,
              pointerEvents: style.opacity.to((v) => (v === 1 ? 'unset' : 'none')),
            }}
            ref={ref}
          >
            {showCloseButton && (
              <span
                className={classNames(
                  `${classPrefix}-close`,
                  'rt-plain-anchor',
                )}
                onClick={() => {
                  onClose?.()
                }}
                role="button"
                tabIndex={0}
                aria-label={translate(('common.close'))}
              >
                <RiCloseLine />
              </span>
            )}
            {body}
          </animated.div>
        </div>
      </div>,
    ),
  )

  return (
    <ShouldRender
      active={active}
      forceRender={forceRender}
      destroyOnClose={destroyOnClose}
    >
      {renderToContainer(getContainer, node)}
    </ShouldRender>
  )
}
